MYSQL 基础语法的详细归纳
- 1. 0X00 前言
- 2. 0X01 基本语法
- 3. 0X02 解决方案
- 4. 0X03 其他细节
- 5. 0X04 总结
- 6. 0X05 参考链接
0X00 前言
做安全最困难的恐怕就是知识点过于杂碎,涉及面过于广阔,所以趁还记得,没事就总结完善备忘一下(参考一些资料,并加入一些自己使用过程中的理解),以防老了失忆,平时短路了拿出来查一下也是不错的。
0X01 基本语法
这一小节主要介绍的是一些增删改查的基本语法
(1)select
mysql 中 select 命令中允许不出现数据库或者数据表的名字,也就是可以没有 from ,这种情况下能将执行结果返回成一个单行单列的表格,例如 select now();返回当前时间。
(2)count()
select count(列) … 可以确定数据表中的记录条数
(3)limit offset,n
limit offset,n 指定输出从 offset 开始的 n 条数据 ,其中 offset 是从 0 开始的,例如 limit 2,2 表示跳过两条数据,从第三条数据开始输出第三第四条数据(offset 不写时,默认为0)
(4)order by
order by column 表示按照某一列对查询结果进行排序
(5)where/having
where/having 表示对行进行筛选,其中 where 的优先级高于 Having ,having 可以和 group by 配合使用,但是 where 不可以
(6)join
select table1.xxx,table2.xxx from table1 left join table2 on table1.yyy=table2.zzz/using(xxx); 是用来扩展查询结果的列的,两个表有键值是关联的,如果没有关联就相当于笛卡尔积,没什么太大的意义
下表是常用的一些语句模型
(7)union
union 实现的是行的扩展,它将两个查询的结果合并在一起,并要求两个查询的列个数相同,数据类型相同,如果不同,系统将会自动将 union 后面的数据的类型转换成前面的对应类型(union 是 mysql 中唯一的集合分隔符)
(8)group by
group by 配合如 count() sum() min() max() 等统计函数 计算有相同字段的行的另一些字段的统计数据,如果是想输出有相同字段的行的另一字段的所有数据,可以使用 group_concat() 这个统计函数,否则只能输出第一个,group by … with rollup 可以在结果的最后一行添加一个计算总和的行
(9)备份与还原数据表
备份:
create table newtable select * from oldtable;
还原:
delete from oldtable;
insert into oldtable select * from newtable;
(10)备份和还原数据库
使用外部工具 mysqldump
备份:
mysqldump -u loginame -p dbname > backupfile
还原:
没有专门的恢复工具,我们直接使用下面的命令
mysql -u loginame -p dbname < backupfile
或者我们直接就能在交互模式下重建
create database dbname;
use dbname;
source backupfile;
(11)插入数据
intert into tablename(column1,column2...) values (value1,value2...),(value3,value4...);
(12)更新数据
update tablename set column1=value1,column2=value2,...where columnID=ID;
(13)删除数据
delect from tablename where ID=ID;
(14)修改数据表
增加一个数据列:
alert table tablename add columnname clotype clooptions;
修改一个数据列:
alert table tablename change oldcolname newcolname coltype coloptions;
(15)information_schema 数据表家族
这是在 mysql5.0 以后引入的机制,有了这个机制,我们能在这些数据表中使用 select 命令检索有关数据库、数据表、数据列的元数据。
这是一个非常重要的机制,最明显的体现就在于使用 sqlmap 跑 MySQL 数据库的时候为什么那么方便,分分钟就能把所有的数据库和表列出来,有些名字甚至是很复杂的都没有丝毫的影响,但是如果你尝试跑 sql server 的数据库,你就会发现情况大不相同, sqlmap 会告诉你需要进行暴力破解,然后就会从他自带的字典中开始遍历,不仅时间非常的长,而且效果也很差,一些自定义的不常见的名字永远都不可能猜出来。
注意一下, information_schema 这个数据库是虚拟的,是在计算机上没有对应的文件的(我们知道数据库也是一个文件,都存储在计算机上,只不过不是纯文本文件罢了,有时候我们还能利用这个文件进行
getshell操作)
所以我们也不能直接使用 use information_schema 、show databases 、select * from information_schema.schemata 来查看有关信息
但是我们能通过 show tables from information_schema 、show columns from information_schema.columns 查看这个数据库内部的信息
mysql> select * from information_schema.schemata;
+--------------+--------------------+----------------------------+------------------------+----------+
| CATALOG_NAME | SCHEMA_NAME | DEFAULT_CHARACTER_SET_NAME | DEFAULT_COLLATION_NAME | SQL_PATH |
+--------------+--------------------+----------------------------+------------------------+----------+
| def | information_schema | utf8 | utf8_general_ci | NULL |
| def | ctf | utf8 | utf8_general_ci | NULL |
| def | employees | utf8 | utf8_general_ci | NULL |
| def | mysql | utf8 | utf8_general_ci | NULL |
| def | performance_schema | utf8 | utf8_general_ci | NULL |
| def | test | latin1 | latin1_swedish_ci | NULL |
| def | world | utf8 | utf8_general_ci | NULL |
+--------------+--------------------+----------------------------+------------------------+----------+
7 rows in set (0.00 sec)
我们来看看这个数据库中有哪些表,它们分别对应着什么数据
还要补充的一点是,information_schema 数据表对所有用户开放而无视权限
0X02 解决方案
这一小节主要介绍的是一些一些实用的函数或者技巧
1.字符串操作
(1)合并字符串
concat(str1,str,str3...);
concat_ws(separator ,str1 ,str2 ,…);
(2)截取字符串
1.substring(str,pos,n);/substr(str,pos,n);/mid(str,pos,n);
mysql> select substring('www.baidu.com','5',5);
+----------------------------------+
| substring('www.baidu.com','5',5) |
+----------------------------------+
| baidu |
+----------------------------------+
1 row in set (0.00 sec)
注意:
(1)这里的 pos 是从 1 开始的,而不是从 0;
(2)n 不写时表示取到字符串末尾
2.letf(str,len); 返回从第一个字符开始的 len 个字符
3.right(str,len); 返回从最后一个字符开始的len 个字符
4.substring_index(str,delim,count);
mysql> select substring_index('www.baidu.com','.',1);
+----------------------------------------+
| substring_index('www.baidu.com','.',1) |
+----------------------------------------+
| www |
+----------------------------------------+
1 row in set (0.00 sec)
mysql> select substring_index('www.baidu.com','.',2);
+----------------------------------------+
| substring_index('www.baidu.com','.',2) |
+----------------------------------------+
| www.baidu |
+----------------------------------------+
1 row in set (0.00 sec)
mysql> select substring_index('www.baidu.com','.',-2);
+-----------------------------------------+
| substring_index('www.baidu.com','.',-2) |
+-----------------------------------------+
| baidu.com |
+-----------------------------------------+
1 row in set (0.10 sec)
(3)计算字符串长度
char_length()/character_length() 和 length()/octet_length()
注意: char_length()/character_length() 计算的是字符的个数,而length()/octet_length() 计算的是字节的长度
但是我在测试的时候发现即使使用中文两者的结果还是一样的,都是按照字节去计算的,可能需要进一步考证
mysql> select LENGTH('你好');
+----------------+
| LENGTH('你好') |
+----------------+
| 4 |
+----------------+
1 row in set, 1 warning (0.00 sec)
mysql> select CHAR_LENGTH('你好');
+---------------------+
| CHAR_LENGTH('你好') |
+---------------------+
| 4 |
+---------------------+
1 row in set, 1 warning (0.00 sec)
(4)改变存储的字符串
将 colname 列的双引号换成单引号
replace(colname,'"','\'');
(5)判断子字符串在字符串中出现的位置
1.locate()
返回子串 substr 在字符串 str 中第一次出现的位置。如果子串 substr 在 str 中不存在,返回值为 0:
LOCATE(substr,str);
例如:
mysql> SELECT LOCATE('bar', ‘foobarbar');
-> 4
mysql> SELECT LOCATE('xbar', ‘foobar');
-> 0
返回子串 substr 在字符串 str 中的第 pos 位置后第一次出现的位置。如果 substr 不在 str 中返回 0 :
LOCATE(substr,str,pos)
例如:
mysql> SELECT LOCATE('bar', ‘foobarbar',5);
-> 7
2.instr()
在一个字符串 str 中搜索指定的字符 substr,返回其第一次出现的位置 index ,其中 index 从 1 开始,如果不存在返回 0 ;
instr(str,substr);
例如:
instr('123', '12');
->1
instr('123', '23');
->2
3.position()
是 locate() 的别名,用法完全一样
4.find_in_set()
FIND_IN_SET(str,strlist)
假如字符串str在由N子链组成的字符串列表strlist中,则返回值的范围在1到N之间。
一个字符串列表就是一个由一些被‘,’符号分开的自链组成的字符串。
如果第一个参数是一个常数字符串,而第二个是typeSET列,则FIND_IN_SET()函数被优化,使用比特计算。
如果str不在strlist或strlist为空字符串,则返回值为0。
如任意一个参数为NULL,则返回值为NULL。这个函数在第一个参数包含一个逗号(‘,’)时将无法正常运行。
注:strlist:一个由英文逗号“,”链接的字符串,例如:”a,b,c,d”,该字符串形式上类似于SET类型的值被逗号给链接起来。
例如:
SELECT FIND_IN_SET('b','a,b,c,d'); //返回值为2,即第2个值
(6)正则匹配
1.like
like 常用通配符:% 、_ 、escape
% : 匹配0个或任意多个字符
_ : 匹配任意一个字符
escape : 转义字符,可匹配%和_。如SELECT * FROM table_name WHERE column_name LIKE '/%/_%_' ESCAPE'/'
2.regexp/rlike
rlike和regexp:常用通配符:. 、* 、 [] 、 ^ 、 $ 、{n}
. : 匹配任意单个字符
* : 匹配0个或多个前一个得到的字符
[] : 匹配任意一个[]内的字符,[ab]*可匹配空串、a、b、或者由任意个a和b组成的字符串。
^ : 匹配开头,如^s匹配以s或者S开头的字符串。
$ : 匹配结尾,如s$匹配以s结尾的字符串。
{n} : 匹配前一个字符反复n次。
注意:
(1)rlike 和 regexp 并不是完全匹配,而是只要模板匹配字符串的一部分就可以了,但是 like 是完全匹配
(2) Like 和 rlike 和 regexp 都是不区分大小写的
3.字符串的二进制比较
我们想要区分大小写就要通过这种二进制比较的方式
mysql> select "A" = "a";
+-----------+
| "A" = "a" |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)
mysql> select "A" = BINARY "a";
+------------------+
| "A" = BINARY "a" |
+------------------+
| 0 |
+------------------+
1 row in set (0.10 sec)
(7)自定义排序
1.elt()
ELT(N ,str1 ,str2 ,str3 ,…)
函数使用说明:若 N = 1 ,则返回值为 str1 ,若 N = 2 ,则返回值为 str2 ,以此类推。 若 N 小于 1 或大于参数的数目,则返回值为 NULL 。 ELT() 是 FIELD() 的补数
mysql> SELECT ELT(3,'hello','halo','test','world');
+--------------------------------------+
| ELT(3,'hello','halo','test','world') |
+--------------------------------------+
| test |
+--------------------------------------+
1 row in set
盲注中我们可以这样将其作为开关
mysql> select ELT(1,1);
+----------+
| ELT(1,1) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
mysql> select ELT(1,0);
+----------+
| ELT(1,0) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
2.field()
FIELD(str, str1, str2, str3, ……)
order by field(str,str1,str2,str3,str4……),str与str1,str2,str3,str4比较,其中str指的是字段名字,
意为:字段str按照字符串str1,str2,str3,str4的顺序返回查询到的结果集。如果表中str字段值不存在于str1,str2,str3,str4中的记录,放在结果集最前面返回。
假如说表是这样的:
root@localhost|iris>select * from ta;
+----+--------+------+-------+
| id | name | age | class |
+----+--------+------+-------+
| 1 | iris | 11 | a1 |
| 2 | iris | 22 | a2 |
| 3 | seiki | 33 | a3 |
| 4 | seiki | 44 | a4 |
| 5 | xuding | 55 | a5 |
| 6 | xut | 66 | a6 |
| 7 | iris | 12 | a2 |
| 8 | iris | 24 | a4 |
| 9 | seiki | 36 | a6 |
| 10 | seiki | 48 | a8 |
| 11 | xuding | 50 | a0 |
| 12 | xut | 77 | a7 |
+----+--------+------+-------+
12 rows in set (0.00 sec)
按照’seiki’,’iris’,’xut’来排序,结果如下:
root@localhost|iris>select * from ta order by field(name,'seiki','iris','xut');
+----+--------+------+-------+
| id | name | age | class |
+----+--------+------+-------+#不在str1,str2,str3中的内容,放在最前面返回,str值相同按照主键的顺序
| 5 | xuding | 55 | a5 |
| 11 | xuding | 50 | a0 |
| 3 | seiki | 33 | a3 |
| 4 | seiki | 44 | a4 |
| 9 | seiki | 36 | a6 |
| 10 | seiki | 48 | a8 |
| 1 | iris | 11 | a1 |
| 2 | iris | 22 | a2 |
| 7 | iris | 12 | a2 |
| 8 | iris | 24 | a4 |
| 6 | xut | 66 | a6 |
| 12 | xut | 77 | a7 |
+----+--------+------+-------+
12 rows in set (0.00 sec)
当然这是正常的用法,但是其实还可以这样理解,这个函数返回的是str 在后面这些字符串中的索引
mysql> SELECT FIELD('halo','hello','halo','test','world');
+---------------------------------------------+
| FIELD('halo','hello','halo','test','world') |
+---------------------------------------------+
| 2 |
+---------------------------------------------+
1 row in set
其实我们还可以把这个函数看成是一个开关,或许在盲注的时候能代替 if ,例如下面这样
mysql> select FIELD(1,0);
+------------+
| FIELD(1,0) |
+------------+
| 0 |
+------------+
1 row in set (0.00 sec)
mysql> select FIELD(1,1);
+------------+
| FIELD(1,1) |
+------------+
| 1 |
+------------+
1 row in set (0.00 sec)
这里的第二个参数决定了这条语句的执行结果,和 if 还是比较类似的,有时候出现 NULL 的时候还可以配合 isnull() 这个函数将其转换成 布尔值,这里提一下,说不定也能用的到。
2.变量与条件表达式
(1)变量
mysql 5.0 以后开始支持存储过程,变量是存储过程中比较重要的元素,MySQL 的变量分为三个类型
1.变量类型:
1.普通变量: 以@开头,在 sql 连接关闭时失去内容
2.系统变量: 以@@开头,表示的是mysql 服务器的工作状态和属性,许多系统变量有两种形式,一种表示当前连接 @@session.wait_timeout,另一种表示mysql 服务器 @@global.wait_timeout
mysql> select @@session.wait_timeout;
+------------------------+
| @@session.wait_timeout |
+------------------------+
| 28800 |
+------------------------+
1 row in set (0.00 sec)
mysql> select @@global.wait_timeout;
+-----------------------+
| @@global.wait_timeout |
+-----------------------+
| 28800 |
+-----------------------+
1 row in set (0.00 sec)
3.存储过程的局部变量: 没有特殊的标志,只在存储过程内有效
2.变量赋值:
变量赋值有两种方式
1.set 赋值的时候 使用 =
2.select 赋值的时候使用 := 或者是 into
mysql> set @varname = 3;
Query OK, 0 rows affected (0.00 sec)
mysql> select @varname;
+----------+
| @varname |
+----------+
| 3 |
+----------+
1 row in set (0.00 sec)
mysql> select @varname := 4;
+---------------+
| @varname := 4 |
+---------------+
| 4 |
+---------------+
1 row in set (0.00 sec)
mysql> select @varname;
+----------+
| @varname |
+----------+
| 4 |
+----------+
1 row in set (0.00 sec)
mysql> select count(*) from bsqli into @varname;
Query OK, 1 row affected (0.32 sec)
mysql> select @varname;
+----------+
| @varname |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec)
2.变量赋值:
我们可以使用表达式给变量赋值,请看下面的实验
mysql> select ID ,@rownum:=@rownum+1 as rownum from city order by ID limit 10;
+----+--------+
| ID | rownum |
+----+--------+
| 1 | 11 |
| 2 | 12 |
| 3 | 13 |
| 4 | 14 |
| 5 | 15 |
| 6 | 16 |
| 7 | 17 |
| 8 | 18 |
| 9 | 19 |
| 10 | 20 |
+----+--------+
10 rows in set (0.00 sec)
这样就实现了逐个增加,是不是很神奇?我们再来看下面的测试
mysql> select @b:=@b is not null;
+--------------------+
| @b:=@b is not null |
+--------------------+
| 0 |
+--------------------+
1 row in set (0.00 sec)
mysql> select @b:=@b is not null;
+--------------------+
| @b:=@b is not null |
+--------------------+
| 1 |
+--------------------+
1 row in set (0.00 sec)
这个怎么理解呢?我们先来看赋值号后面的这个表达式
@b is not null
由于 @b 并没有在当前的会话中定义过,于是一开始是 null ,然后我们做了个判断,判断 @b 不是 null ,很明显这个得到的是假,也就是 0 ,那么再执行完这个语句之后 @b 就被赋值为 0 ,当第二次再执行的时候就是判断 0 不是 Null ,这次肯定是真,于是返回1 此时 @b 就被赋值为1
这样就达到了在同一个会话中,相同的语句执行结果不同的效果,之前在 LCTF2018 中有一道题就是利用这种方式去做的,但是没人解出来。
(2)条件表达式
1.if(condition,result1,result2);
当 condition 结果为真时返回 result1 否则返回 result2,这常常用作我们盲注的开关函数,
mysql> select if(1,1,0);
+-----------+
| if(1,1,0) |
+-----------+
| 1 |
+-----------+
1 row in set (0.00 sec)
mysql> select if(0,1,0);
+-----------+
| if(0,1,0) |
+-----------+
| 0 |
+-----------+
1 row in set (0.00 sec)
2.case(…)when…then…else…end
case 有两种变体
(1)case expr when val1 then result1 when val2 then result2 else result3 end;
就是会判断 expr 的结果 ,根据结果是 val1 还是 val2 或者其他,来执行不同的语句
(2)case when condition1 then result1 when condition2 result2 else result3 end;
这种就是不判断值,直接根据不同情况进行选择
比如可以构造这样一条注入语句
select * from test where id =-1 union select 1,case when username like 'a%' then 0 else 2222222222222222222 end,3,4 from tdb_admin
3.子查询
(1)模板一
select ... from ... where col = [any|all](select...);
1.在这个变体里,子查询的返回结果必须是一个离散值(一行一列),用来和 col 进行比较操作,这里的比较操作符=还可以是 > >= < <= <>
2.当前面有修饰符的时候,前面的修饰符可以是 any/some( 这两个同义)和 all,这时子查询可以返回多个值,col = any… 等同于 col = in … ,当前面的修饰符是 all 的时候只有两种情况能让 operator all 的返回结果为真,一种是在所有的值都能满足这个 operator 条件时,另一种是在 select 子查询没有任何返回结果时
(2)模板二
select ....from ... where col [not]in(select...);
此时select 子查询的返回结果可以是一个离散值的列表
(3)模板三
select row(value1,valu2...) = [any/some](select col1,col2...);
该语句查询数据表中师傅存在一条对应的记录,子查询返回的结果是一组离散值,但是当使用 any 或者其同义词 some 修饰的时候可以返回多组离散值,其中只要有一组满足条件结果则为真
(4)模板四
select ... from ...where col [not]exists(select...);
(5)模板五
select ... from (select ...) as name where ...
外层 select 查询的是内层 select 查询出来的临时表,MySQL 要求这种临时表必须通过 as name 的形式给予别名
注意: mysql 不允许在子查询中使用 limit ,limit 一定是在最外层并且最后运行的
(6)补充
当然除了上面这几种类别以外,子查询的位置还能放在 select 后面作为选择列,或者放在 order by 后面作为拍序列,但是在 sql 注入中子查询的缺点是不能回显,union 倒是可以,只要有回显位,但是现在能回显的越来越少了,一般都是盲注什么的。
0X03 其他细节
(1)命名规则:
数据库、数据表、数据字段等数据库对象的名字长度最多达到64字符,允许使用的字符是 MySQL 允许使用的字符集中的所有数字字母符号以及 _ 和 $
注意:
(1)包含特殊字符和保留字的名字一般不被使用,但是还是可以通过添加 反引号的方式来使用,但是同样这样的字段是没法直接用命令行查询的,查询的时候也要带上反引号
(2)当使用不在该数据库的数据表的时候,要使用 数据库.数据表 的形式,同理,如果遇到不能区分的列,也要使用类似的方式进行区分
(2)字符串
字符串可以用单引号包裹也可以用双引号,当出现引号里面还有引号的使用,必须使用不同的引号加以区分,或者使用反斜杠进行转义
MySQL 支持使用 16进制值表示对象
(3)数值
1.MySQL 支持十进制使用小数点,并且可以使用 用e 表示的科学计数法表示很大或者很小的数值
2.MYSQL 支持使用 0x 或者 x’12121’ 表示16进制数据
mysql> select 0x4142434445464748494a;
+------------------------+
| 0x4142434445464748494a |
+------------------------+
| ABCDEFGHIJ |
+------------------------+
1 row in set (0.00 sec)
mysql> select x'4142434445464748494a';
+-------------------------+
| x'4142434445464748494a' |
+-------------------------+
| ABCDEFGHIJ |
+-------------------------+
1 row in set (0.00 sec)
3.MySQL 支持进制之间的隐式转换,比如 16进制加一个十进制的数会变成十进制
mysql> select 0x41;
+------+
| 0x41 |
+------+
| A |
+------+
1 row in set (0.00 sec)
mysql> select 0x41+0;
+--------+
| 0x41+0 |
+--------+
| 65 |
+--------+
1 row in set (0.00 sec)
4.MySQL 支持字符串和数值之间的转换,字符串在和数值运算时会将字符串转换成数值(开头部分是数值则转化成对应数值,如果不是数值则自动转化成0)
mysql> select "112.3456asdasd"+1;
+--------------------+
| "112.3456asdasd"+1 |
+--------------------+
| 113.3456 |
+--------------------+
1 row in set, 1 warning (0.00 sec)
5.MySQL 5.03 开始支持二进制数值,可以表示为 b’100110’ 的形式
(4)注释
1.单行注释:# 和 – (后面有一个空格)
2.多行注释:/*/
3.内联注释:`/!*/`
(5)操作符
注意:逻辑与的优先级要高于逻辑或
(6)一些函数
1.比较、测试、分支
2.字符串处理
3.统计函数(与 group by 配合使用)
4.算数函数
5.其他函数
6.mysql 运算符的优先级
0X04 总结
因为最近心血来潮想写一点关于 SQL 的东西,中间一些知识点不想再过多介绍,于是就打算偷个小懒先把这篇老文章丢出来,到时候直接参考。
0X05 参考链接
https://www.cnblogs.com/xiaoxi/p/5889486.html
https://www.cnblogs.com/tommy-huang/p/4483583.html
https://blog.csdn.net/dongganen/article/details/78580813
https://www.cnblogs.com/xdans/p/5412468.html